// 实现异步工作器
#pragma once
#include "buffer.hpp"
// #include "Mutex.hpp"
#include <mutex>
#include <condition_variable>
#include <thread>

#include <functional>
using namespace std;
namespace LOG
{
    enum class AysnType
    {
        Aysn_Safe,//安全状态buffer空间满的话就退出
        Aysn_Unsafe  //buffer空间满的话就扩容

    };
    class AsyncLopper
    {
        using fun_t = function<void(Buffer &)>;

    public:
        AsyncLopper(const fun_t cb) : _stop(false), _thread(thread(&AsyncLopper::ThreadEntry,this)), _callback(cb) {}; // 线程初始化，第一个参数为线程执行的函数，后面的为要传入线程执行函数的参数是什么
        ~AsyncLopper() { stop(); }
        void stop()
        {
            _stop = true;
            _cond_con.notify_all();
            _thread.join();//等待线程退出
        }
        void push(const char *data, size_t len)
        {
            // 第一步肯定是加锁
            unique_lock<mutex> lock(_mutex);
            // 第二步是条件变量的等待 缓冲区的可写的空间要大于要写入的空间
            _cond_pro.wait(lock, [&]() { return len <= _pro.writeAbleSize(); });
            // 第三步向缓冲区里面写数据
            std::cout << "push data:[" << data << "]" << std::endl;
            _pro.push(data, len);
            // 第四步为唤醒消费者
            _cond_con.notify_all();
            std::cout << "weakup consumer...\n";
        }

    private:
        void ThreadEntry()
        {
            //所以我怀疑是这里上边push了数据后程序就退出，但是下边这里刚循环上来stop被置为true所以退出了
            while (true) //还是有退出的情况，这里用死循环，这内部进行被唤醒后再做判断
            {
                {
                    std::cout << "wait productor...\n";
                    // 第一步肯定要申请锁
                    unique_lock<mutex> lock(_mutex);   
                    // 第二步为判断生产缓冲区是否为空不为空就交换两个缓冲区
                    _cond_con.wait(lock, [&]() { return  _stop |!_pro.empty(); });
                    if (_stop && _pro.empty()) {
                        std::cout << "无数据需要处理，退出工作线程\n";
                        break;
                    }
                    std::cout << "swap:" << _con.readeAbleSize() << " - " << _pro.readeAbleSize() << std::endl;
                    _con.Swap(_pro); //因为是传只，所以这里并没有吧——pro清空
                }
                _callback(_con);        // 处理缓冲区
                _con.reset();           // 处理完然后重置缓冲区
                _cond_pro.notify_all(); // 唤醒生产者可以重新生产了
            }
        }

    private:

        // 需要一个锁
        // mutex _mutex;
        std::mutex _mutex;
        // 两个缓冲区 一个是生产者的缓冲区 一个为消费者的缓冲区
        Buffer _pro;
        Buffer _con;
        // 需要两个条件变量分别为生产者和消费者的条件变量
        condition_variable _cond_pro;
        condition_variable _cond_con;
        // 然后需要一个线程
        std::thread _thread;
        // 最后需要一个状态表示工作器是否是启动的
        bool _stop;
        fun_t _callback;
        AysnType type;
    };
}
// 异步工作器：
// 1. 异步工作使用双缓冲区思想
// 外界将任务数据，添加到输入缓冲区中，
// 异步线程对处理缓冲区中的数据进行处理，若处理缓冲区中没有数据了则交换缓冲区
// 实现：
// 管理的成员：
// 1. 双缓冲区（生产，消费）
// 2. 互斥锁//保证线程安全
// 3. 条件变量-生产＆消费（生产缓冲区没有数据，处理完消费缓冲区数据后就休眠）
// 4. 回调函数（针对缓冲区中数据的处理接口-外界传入一个函数，告诉异步工作器数据该如何处理）提供的操作：
// 1.停止异步工作器
// 2. 添加数据到缓冲区
// 私有操作：
// 创建线程，线程入口函数中，交换缓冲区，对消费缓冲区数据使用回调函数进行处理，处理完后再次交换